<template>
  <div class="box">
    <div id="map"
         ref='map'>
    </div>
    <div class="distance"
         v-if="distance !== 0 ">
      总距离：{{ distance }} km
    </div>
    <button class="play"
            @click="animateFunc()">播放轨迹</button>
    <div ref='marker'
         class="marker"
         v-show='showDetail'
         @click.stop="showDetail = false">
      <span>车牌：粤ABCDE</span>
      <span>型号：本田</span>
      <span>颜色：白色</span>
      <span>剩余油量：200L</span>
      <span>距离终点距离：{{ lastKM }}km</span>
      <span>距离终点时间：18min</span>
    </div>
  </div>
</template>

<script>
import mapboxgl from 'mapbox-gl'

export default {
  data() {
    return {
      map: null, // mapbox 对象
      pointsGeojson: {
        type: 'FeatureCollection',
        features: [],
      }, // 点数据
      carGeojson: {
        type: 'FeatureCollection',
        features: [
          {
            type: 'Feature',
            geometry: {
              type: 'Point',
              coordinates: [],
            },
            properties: { bearing: null },
          },
        ],
      }, // 汽车数据
      showDetail: false,
      // isPlay: false, // 播放状态
      lastKM: 0,
      aaa: {
        type: 'Feature',
        geometry: {
          type: 'LineString',
          coordinates: [],
        },
      },
      delCount: 1,
    }
  },
  computed: {
    // 线数据
    linestringGeojson() {
      const hasCoordinates = this.pointsGeojson.features.length !== 0
      return {
        type: 'Feature',
        geometry: {
          type: 'LineString',
          coordinates: hasCoordinates
            ? this.pointsGeojson.features.map((v) => v.geometry.coordinates)
            : [],
        },
      }
    },
    // 总距离
    distance() {
      const hasCoordinates = this.pointsGeojson.features.length !== 0
      return hasCoordinates ? this.$turf.length(this.linestringGeojson) : 0
    },
    // 汽车角度
    carBearing() {
      return this.carGeojson.features[0].properties.bearing
    },
  },
  /* 实时更新 style-source */
  watch: {
    linestringGeojson: {
      deep: true,
      handler(val) {
        this.map.getSource('lines-geojson').setData(val)
      },
    },
    pointsGeojson: {
      deep: true,
      handler(val) {
        this.map.getSource('points-geojson').setData(val)
        const hasFirst = val.features.length !== 0
        // 刷新 car
        if (hasFirst) {
          const carGeojson = this.carGeojson
          carGeojson.features[0].geometry.coordinates =
            val.features[0].geometry.coordinates
          carGeojson.features[0].properties.bearing = 0
          this.carGeojson = carGeojson
        }
      },
    },
    carGeojson: {
      deep: true,
      handler(val) {
        this.map.getSource('car-geojson').setData(val)
      },
    },
    carBearing(newVal, oldVal) {
      // console.log(newVal, oldVal)
      const change = Math.abs(
        Math.floor(oldVal).toFixed(2) - Math.floor(newVal).toFixed(2)
      )
      // if (aa !== 0) console.log(aa)
      if (oldVal && change > 0.1) this.delCount += 0.5
      // console.log(this.delCount)
    },
  },
  mounted() {
    this.mapInitFunc()
    this.mapOnLoadFunc()
  },
  methods: {
    /**
     * mapbox 初始化
     * ! 1. 显示地图
     *  */ 
    mapInitFunc() {
      mapboxgl.accessToken =
        'pk.eyJ1Ijoic2FnaXQtemgiLCJhIjoiY2t0bno1cGp6MDdxNjJubGdzZHQxOTVtaiJ9.shmGZyFnHkC2rc5d5dHgBA'
      const map = new mapboxgl.Map({
        container: 'map',
        center: [113.3089, 23.0654],
        style: 'mapbox://styles/mapbox/streets-v11',
        hash: true,
        zoom: 9,
      })
      this.map = map
    },
    /* mapbox 配置 */
    mapOnLoadFunc() {
      this.map.on('load', () => {
        /**
         * ! addSource
         *  */
        this.map.addSource('points-geojson', {
          type: 'geojson',
          data: this.pointsGeojson,
        })

        this.map.addSource('lines-geojson', {
          type: 'geojson',
          data: this.linestringGeojson,
        })

        this.map.addSource('car-geojson', {
          type: 'geojson',
          data: this.carGeojson,
        })

        /**
         * ! addLayer
         *  */

        this.map.addLayer({
          id: 'points',
          type: 'circle',
          source: 'points-geojson',
          paint: { 'circle-radius': 5, 'circle-color': '#000' },
          filter: ['in', '$type', 'Point'],
        })

        this.map.addLayer({
          id: 'lines',
          type: 'line',
          source: 'lines-geojson',
          layout: { 'line-cap': 'round', 'line-join': 'round' },
          paint: { 'line-color': '#000', 'line-width': 2.5 },
          filter: ['in', '$type', 'LineString'],
        })

        this.map.addLayer({
          id: 'car',
          source: 'car-geojson',
          type: 'symbol',
          layout: {
            'icon-image': 'airport-15',
            'icon-rotate': ['get', 'bearing'],
            'icon-rotation-alignment': 'map',
            'icon-allow-overlap': true,
            'icon-ignore-placement': true,
          },
        })

        this.mapOnClickFunc()
      })
    },
    /** 
     * 点击事件
     * ! 2. 生成轨迹线路
     */
    mapOnClickFunc() {
      this.map.on('click', (e) => {
        const monument = [e.lngLat.lng, e.lngLat.lat] // 点击坐标
        this.showDetail = false

        // 查找 car
        const carFeatures = this.map.queryRenderedFeatures(e.point, {
          layers: ['car'],
        })

        // 点击汽车
        if (carFeatures.length) {
          this.handleShowDetail(monument)
          /**
           * ! 计算剩余距离 
           *  */ 
          const obj = JSON.parse(JSON.stringify(this.linestringGeojson))
          obj.geometry.coordinates.splice(0, this.delCount)
          obj.geometry.coordinates.unshift(monument)
          this.lastKM = this.$turf.length(obj)

          this.showDetail = true
          return false
        }

        // 查找点
        const pointsFeatures = this.map.queryRenderedFeatures(e.point, {
          layers: ['points'],
        })

        // 删除点
        if (pointsFeatures.length) {
          const { id } = pointsFeatures[0].properties
          this.pointsGeojson.features = this.pointsGeojson.features.filter(
            (v) => v.properties.id !== id
          )
          return false
        }

        // 添加点
        const point = {
          type: 'Feature',
          geometry: {
            type: 'Point',
            coordinates: monument,
          },
          properties: { id: String(new Date().getTime()) },
        }
        this.pointsGeojson.features.push(point)
      })
    },
    /** 
     * ! 4. 播放轨迹
     *  */
    animateFunc() {
      const step = 500 // 步长

      let counter = 0 // 下标

      // 用于存储路线坐标的数组
      const arc = []
      for (let i = 0; i < this.distance; i += this.distance / step) {
        const setpar = this.$turf.along(this.linestringGeojson, i)
        arc.push(setpar.geometry.coordinates)
      }

      // 落点
      arc.push(
        this.pointsGeojson.features[this.pointsGeojson.features.length - 1]
          .geometry.coordinates
      )
      // console.log(arc.length)

      const animate = () => {
        // 起始点
        const start = arc[counter]
        const end = arc[counter + 1]
        if (!end || !start) return false

        const carGeojson = this.carGeojson
        // 坐标
        carGeojson.features[0].geometry.coordinates = start
        // 角度
        const bearing = this.$turf.bearing(
          this.$turf.point(start),
          this.$turf.point(end)
        )
        carGeojson.features[0].properties.bearing = bearing

        // 更新
        this.carGeojson = carGeojson
        this.map.getSource('car-geojson').setData(carGeojson)

        // 动画
        if (counter < step) requestAnimationFrame(animate)

        counter++
      }
      animate()
    },
    /**  
     * ! 5. 显示信息牌 
     */
    handleShowDetail(monument) {
      const el = this.$refs.marker

      new mapboxgl.Marker(el).setLngLat(monument).addTo(this.map)
    },
  },
}
</script>

<style scoped>
#map {
  margin: 0 atuo;
  width: 1000px;
  height: 500px;
}

.distance {
  position: fixed;
  top: 10px;
  left: 10px;
  z-index: 1;
  background-color: rgba(0, 0, 0, 0.5);
  color: #fff;
  font-size: 11px;
  line-height: 18px;
  display: block;
  margin: 0;
  padding: 5px 10px;
  border-radius: 3px;
}

.play {
  position: fixed;
  top: 50px;
  left: 10px;
  background-color: #3386c0;
  color: #fff;
  display: inline-block;
  margin: 0;
  padding: 10px 20px;
  border: none;
  cursor: pointer;
  border-radius: 3px;
}

.marker {
  /* background-image: url('https://docs.mapbox.com/mapbox-gl-js/assets/washington-monument.jpg'); */
  background-color: rgba(0, 0, 0, 0.5);
  /* background-size: cover; */
  /* width: 100px; */
  padding: 0 20px;
  height: 200px;
  border-radius: 5px;
  /* cursor: pointer; */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: space-evenly;
  color: #fff;
}

.popup {
  max-width: 200px;
}
</style>